'''
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http:#www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
'''
from base.datatypes.xsd.XSDDatatypeInstances import XSDDatatypeInstances

'''
 * Represent an XSD date/time value. Rather than have a separate type for each
 * legal date/time value combination this is a combination type than does runtime
 * checks whether a given field is legal in the current circumstances.
'''

from base.datatypes.xsd.AbstractDateTime import AbstractDateTime
# from base.datatypes.xsd.XSDDatatype import XSDDatatype
from base.exception.IllegalDateTimeFieldException import IllegalDateTimeFieldException
from base.java.StringBuilder import StringBuilder
from base.datatypes.xsd.impl.XSDAbstractDateTimeType import XSDAbstractDateTimeType

class XSDDateTime(AbstractDateTime):
    '''*
     * Mask to indicate whether year is present
    '''
    # 5个short
    YEAR_MASK = 0x1

    '''*
     * Mask to indicate whether month is present
    '''
    MONTH_MASK = 0x2

    '''*
     * Mask to indicate whether day is present
    '''
    DAY_MASK = 0x4

    '''*
     * Mask to indicate whether time is present
    '''
    TIME_MASK = 0x8

    '''*
     * Mask to indicate all date/time are present
    '''
    FULL_MASK = 0xf

    # Unused '''* table mapping xs type name to mask of legal values'''
    #HashMap maskMap = new HashMap()

    '''*
     * Set of legal fields for the particular date/time instance
    '''
    # protected short
    mask = None

    '''*
     * Constructor - should only be used by the internals but # param
    # return
    scope because
     * the internals spread across multiple packages.
     *
     * @param value the date/time value returned by the parsing
     * @param mask  bitmask defining which components are valid in this instance
     *              (e.g. dates don't have valid time fields).
    '''
    # param Object int
    def __init__(self, value, mask):
        super().__init__(value)
        self.mask = mask


    '''*
     * Constructor - create a full DateTime object from a java calendar instance.
     *
     * @param date java calendar instance
    '''
    # param
    # return
    def initDate(self, date):
        super().__init__(self.convertCalendar(date))
        self.mask = self.FULL_MASK    

    '''*
     * Return the most specific xsd type which can represent
     * this date/time
    '''
    # param
    # return XSDDatatype
    def getNarrowedDatatype(self):
        if self.mask == XSDDateTime.TIME_MASK:
            return XSDDatatypeInstances.XSDtime
        elif self.mask == XSDDateTime.MONTH_MASK:
            return XSDDatatypeInstances.XSDgMonth
        elif self.mask == XSDDateTime.DAY_MASK:
            return XSDDatatypeInstances.XSDgDay
        elif self.mask == XSDDateTime.YEAR_MASK:
            return XSDDatatypeInstances.XSDgYear
        elif self.mask == (XSDDateTime.MONTH_MASK | XSDDateTime.DAY_MASK):
            return XSDDatatypeInstances.XSDgMonthDay
        elif self.mask == (XSDDateTime.MONTH_MASK | XSDDateTime.YEAR_MASK):
            return XSDDatatypeInstances.XSDgYearMonth
        elif self.mask == (XSDDateTime.MONTH_MASK | XSDDateTime.YEAR_MASK | XSDDateTime.DAY_MASK):
            return XSDDatatypeInstances.XSDdate
        else:
            return XSDDatatypeInstances.XSDdateTime
        
    

    '''*
     * Set the mask for this date/time to be that appropriate
     * for the given XSD subtype. If the type is a subtype of XSDdateTime the
     * mask will be narrowed appropriately, other types will be silently ignored.
    '''
    # param XSDDatatype
    # return void
    def narrowType(dt):
        if (dt.equals(XSDDatatypeInstances.XSDtime)):
            mask = XSDDateTime.TIME_MASK
        elif (dt.equals(XSDDatatypeInstances.XSDgMonth)):
            mask = XSDDateTime.MONTH_MASK
        elif (dt.equals(XSDDatatypeInstances.XSDgDay)):
            mask = XSDDateTime.DAY_MASK
        elif (dt.equals(XSDDatatypeInstances.XSDgYear)):
            mask = XSDDateTime.YEAR_MASK
        elif (dt.equals(XSDDatatypeInstances.XSDgMonthDay)):
            mask = XSDDateTime.MONTH_MASK | XSDDateTime.DAY_MASK
        elif (dt.equals(XSDDatatypeInstances.XSDgYearMonth)):
            mask = XSDDateTime.YEAR_MASK | XSDDateTime.MONTH_MASK
        elif (dt.equals(XSDDatatypeInstances.XSDdate)):
            mask = XSDDateTime.MONTH_MASK | XSDDateTime.YEAR_MASK | XSDDateTime.DAY_MASK
        
    

    '''*
     * Convert a java calendar object to a new int[] in the format used by XSDAbstractDateTime
    '''
    # param Calendar
    # return int[]
    # private
    @staticmethod
    def convertCalendar(date):
        # #int[] data = new int[TOTAL_SIZE]
        # data = []
        #
        # # Calendar
        # cal = date.clone()
        # # By observation (Sun Java 6), this is necessary (to force internal calculations presumably) ...
        # cal.get(Calendar.ZONE_OFFSET)
        # # ... then we can rebase the calendar
        # cal.setTimeZone(TimeZone.getTimeZone("GMT"))
        #
        # data[AbstractDateTime.CY] = cal.get(Calendar.YEAR)
        # data[AbstractDateTime.M] = cal.get(Calendar.MONTH) + 1
        # data[AbstractDateTime.D] = cal.get(Calendar.DAY_OF_MONTH)
        # data[AbstractDateTime.h] = cal.get(Calendar.HOUR_OF_DAY)
        # data[AbstractDateTime.m] = cal.get(Calendar.MINUTE)
        # data[AbstractDateTime.s] = cal.get(Calendar.SECOND)
        # data[AbstractDateTime.utc] = 'Z'
        #
        # # int
        # ms = cal.get(Calendar.MILLISECOND)
        # # Store value in a canonical form - no scale for trailing zeros.
        # # This must align with the string parsing code.
        # if (ms == 0):
        #     data[AbstractDateTime.ms] = 0
        #     data[AbstractDateTime.msscale] = 0
        # elif (ms % 100 == 0):
        #     data[AbstractDateTime.ms] = ms / 100
        #     data[AbstractDateTime.msscale] = 1
        # elif (ms % 10 == 0):
        #     data[AbstractDateTime.ms] = ms / 10
        #     data[AbstractDateTime.msscale] = 2
        # else:
        #     data[AbstractDateTime.ms] = ms
        #     data[AbstractDateTime.msscale] = 3
        #
        # return data
        raise RuntimeError('unsupported method')
    

    '''*
     * Return the date time as a java Calendar object.
     * If the timezone has been specified then the object is normalized to GMT.
     * If the zone has not been specified then we use the default timezone.
     *
     * @throws IllegalDateTimeFieldException if this is not a full date + time
    '''
    # param
    # return Calendar
    # throws IllegalDateTimeFieldException
    def asCalendar(self):
        # TimeZone tz = data[utc] == 'Z' ? TimeZone.getTimeZone("GMT") : TimeZone.getDefault()
        # Calendar calendar = new GregorianCalendar(tz)
        # calendar.set(data[CY], data[M] - 1, data[D], data[h], data[m], data[s])
        # calendar.set(Calendar.MILLISECOND, (int) Math.round(1000.0 * fractionalSeconds))
        # # was this to work around problems with some Linux JDKs
        # # calendar.set(Calendar.MILLISECOND, 0)
        # return calendar
        raise RuntimeError("unsupported method")
    

    '''*
     * Return the number of years in the dateTime.
     *
     * @throws IllegalDateTimeFieldException if there is no legal year component
    '''
    # param
    # return int 
    # throws IllegalDateTimeFieldException
    def getYears(self):
        global CY
        if ((self.mask & XSDDateTime.YEAR_MASK) == 0):
            raise IllegalDateTimeFieldException("Year not available")
        return self.data[CY]
    

    '''*
     * Return the month in the dateTime, this is in ISO8601 format so january = 1
     *
     * @throws IllegalDateTimeFieldException if there is no legal month component
    '''
    # param
    # return int 
    # throws IllegalDateTimeFieldException
    def getMonths(self):
        global M
        if ((self.mask & XSDDateTime.MONTH_MASK) == 0):
            raise IllegalDateTimeFieldException("Month not available")
        return self.data[M]
    

    '''*
     * Return the number of years in the dateTime
     *
     * @throws IllegalDateTimeFieldException if there is no legal day component
    '''
    # param
    # return int 
    # throws IllegalDateTimeFieldException
    def getDays(self):
        global D
        if ((self.mask & XSDDateTime.DAY_MASK) == 0):
            raise IllegalDateTimeFieldException("Day not available")
        return self.data[D]
    

    '''*
     * Return the number of hours in the dateTime
     *
     * @throws IllegalDateTimeFieldException if there is no legal time component
    '''
    # param
    # return
    # throws IllegalDateTimeFieldException
    def getHours(self):
        global h
        if ((self.mask & XSDDateTime.TIME_MASK) == 0):
            raise IllegalDateTimeFieldException("Time not available")
        return self.data[h]

    '''*
     * Return the number of minutes in the dateTime
     *
     * @throws IllegalDateTimeFieldException if there is no legal time component
    '''
    # param
    # return
    # throws IllegalDateTimeFieldException
    def getMinutes(self):
        global m
        if ((self.mask & XSDDateTime.TIME_MASK) == 0):
            raise IllegalDateTimeFieldException("Time not available")
        return self.data[m]
    

    '''*
     * Return the number of full seconds in the dateTime
     *
     * @throws IllegalDateTimeFieldException if there is no legal time component
    '''
    # param
    # return
    # throws IllegalDateTimeFieldException
    def getFullSeconds(self):
        global s
        if ((self.mask & XSDDateTime.TIME_MASK) == 0):
            raise IllegalDateTimeFieldException("Time not available")
        return self.data[s]
    

    '''*
     * Return the number of seconds in the dateTime, including fractional part
     *
     * @throws IllegalDateTimeFieldException if there is no legal time component
    '''
    # param
    # return double
    # throws IllegalDateTimeFieldException
    def getSeconds(self):
        global s
        if ((self.mask & XSDDateTime.TIME_MASK) == 0):
            raise IllegalDateTimeFieldException("Time not available")
        return self.data[s] + self.fractionalSeconds
    

    '''*
     * Return the time component of the dateTime - i.e. just the hours/mins/seconds,
     * and returns the values in seconds.
     *
     * @throws IllegalDateTimeFieldException if there is no legal time component
    '''
    # param
    # return double
    # throws IllegalDateTimeFieldException
    def getTimePart(self):
        global h
        global m
        if ((self.mask & XSDDateTime.TIME_MASK) == 0):
            raise IllegalDateTimeFieldException("Time not available")
        return ((self.data[h]) * 60 + self.data[m]) * 60 + self.getSeconds()
    

    '''*
     * Return legal serialized form.
    '''
    # @Override
    # param
    # return String
    def toString(self):
        # StringBuilder
        buff = StringBuilder('')
        if ((self.mask & XSDDateTime.YEAR_MASK) != 0):
            # 两个 int
            cy = self.data[CY]
            absCY = abs(cy)
            if (cy < 0):
                buff.append('-')
            # XSD dateTime requires at least 4 digits for the year field
            if (absCY < 10):
                    buff.append('0')
            if (absCY < 100):
                    buff.append('0')
            if (absCY < 1000):
                    buff.append('0')
            buff.append(absCY)
        else:
            buff.append("-")
        
        if ((self.mask & (XSDDateTime.MONTH_MASK | XSDDateTime.DAY_MASK)) != 0):
            buff.append("-")
            if ((self.mask & XSDDateTime.MONTH_MASK) != 0):
                if (self.data[M] <= 9):
                    buff.append('0')
                buff.append(self.data[M])
            else:
                buff.append("-")
            
            if ((self.mask & XSDDateTime.DAY_MASK) != 0):
                if (self.mask != XSDDateTime.DAY_MASK):
                    buff.append('-')
                if (self.data[D] <= 9):
                    buff.append('0')
                buff.append(self.data[D])
            
        
        if ((self.mask & XSDDateTime.TIME_MASK) != 0):
            buff.append('T')
            buff.append(self.timeLexicalForm())
        
        global utc
        if (self.data[utc] != 0):
            buff.append('Z')

        return buff.toString()
    

    '''*
     * Return the lexical form of the time component.
    '''
    # param
    # return String
    def timeLexicalForm(self):
        # StringBuffer
        buff = StringBuilder('')
        if (self.data[h] < 10):
            buff.append("0")
        buff.append(self.data[h])

        buff.append(":")
        if (self.data[m] < 10):
            buff.append("0")
        buff.append(self.data[m])

        buff.append(":")
        if (self.data[s] < 10):
            buff.append("0")
        buff.append(self.data[s])

        global ms
        global msscale
        if (self.data[ms] != 0):
            buff.append(".")
            XSDAbstractDateTimeType.appendFractionalTime(buff, self.data[ms], self.data[msscale])
        
        return buff.toString()
    


